package ws.jdbc.concurrent.driver.sqlserver.tds;

import java.io.IOException;
import java.nio.ByteBuffer;
import ws.jdbc.concurrent.driver.sqlserver.tds.datatypes.FIXEDLENTYPE;
import ws.jdbc.concurrent.driver.sqlserver.tds.datatypes.TYPE_INFO;
import ws.jdbc.concurrent.driver.sqlserver.tds.datatypes.TYPE_VARBYTE;
import ws.jdbc.concurrent.driver.sqlserver.tds.datatypes.TYPE_VARLEN;
import ws.jdbc.concurrent.driver.sqlserver.tds.datatypes.TypeInfoFixedLenType;
import ws.jdbc.concurrent.driver.sqlserver.tds.datatypes.TypeInfoVarLenType;
import ws.jdbc.concurrent.driver.sqlserver.tds.datatypes.VARLENTYPE;

/**
 * This data stream reader reads data types from a data stream.
 *
 * <p><i>
 * The actual type of a given variable data type stream dependent on the type
 * of the data being sent within the message as defined in the TYPE_INFO rule.
 * </p></i>
 * 
 * @author Wilson Santos, 36228 <wilson.santos@ua.pt>
 */
public class DataTypeDependentDataStreamReader extends StreamReader {

  /**
   * Create a new reader instance.
   *
   * @param stream data input stream to read the token from.
   */
  DataTypeDependentDataStreamReader(final ByteBuffer stream) {
    super(stream);
  }

  /**
   * Read a TYPE_INFO object from the input stream.
   *
   * @return the TYPE_INFO object read.
   *
   * @throws IOException if an error occurs while reading from the data input stream.
   */
  public TYPE_INFO readTypeInfo() throws IOException {
    TYPE_INFO ret = null;

    byte dataType = stream.get();
    bytesRead += 1;

    if (VARLENTYPE.isVarlenType(dataType)) {
      ret = new TypeInfoVarLenType(dataType);
      bytesRead += ret.read(stream);
    } else if (FIXEDLENTYPE.isFixedLenType(dataType)) {
      ret = new TypeInfoFixedLenType(dataType);
    }
    return ret;
  }

  /**
   * Read a TYPE_VARBYTE object from the input stream.
   *
   * @param typeInfo TYPE_INFO object previously read. This object object helps to
   * determine stream we are going to read the bytes for a fixed-length or a variable-length
   * data type.
   *
   * @return the TYPE_VARBYTE object read.
   *
   * @throws IOException if an error occurs while reading from the data input stream.
   */
  public TYPE_VARBYTE readTypeVarByte(TYPE_INFO typeInfo) throws IOException {
    TYPE_VARBYTE ret = null;

    if (VARLENTYPE.isVarlenType(typeInfo.getDataType())) {
      /* Read variable-length data type. */

      TYPE_VARLEN typeVarlen = TYPE_VARLEN.read(stream, typeInfo.getDataType());
      bytesRead += typeVarlen.size();

      if (!typeVarlen.wasNull()) {
        byte[] data = new byte[typeVarlen.getLength()];
        stream.get(data, 0, data.length);
        bytesRead += data.length;
        ret = new TYPE_VARBYTE(typeVarlen, data);
      } else {
        ret = new TYPE_VARBYTE(typeVarlen, null);
      }
    } else if (FIXEDLENTYPE.isFixedLenType(typeInfo.getDataType())) {
      /* Read fixed-length data type. */
      byte[] data = FIXEDLENTYPE.read(stream, typeInfo.getDataType());
      bytesRead += data.length;
      ret = new TYPE_VARBYTE(data);
    } else {
      throw new IllegalArgumentException("Error: unknown data type (0x" + typeInfo.getDataType() + ")");
    }

    return ret;
  }
}
